package org.caw.ws.providers;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import org.caw.exception.CawException;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Provider
@Produces({ "application/json" })
public class CawExceptionProvider implements MessageBodyWriter<CawException> {

	private static final String EXCEPTION = "exception";
	private static final String EXCEPTION_CLASS = "exceptionClass";
	private static final String MESSAGE = "message";
	private static final String STACK_TRACE = "stackTrace";

	private static final Logger LOGGER = LoggerFactory.getLogger(CawExceptionProvider.class);

	@Override
	public long getSize(CawException exception, Class<?> clazz, Type type, Annotation[] anotation, MediaType mediaType) {
		return stringify(exception, mediaType).length();
	}

	@Override
	public boolean isWriteable(Class<?> clazz, Type type, Annotation[] annotation, MediaType mediaType) {
		Class<?> iteratorClazz = clazz;
		while (!iteratorClazz.getName().equals(Object.class.getName())) {
			if (iteratorClazz.getName().equals(CawException.class.getName())) {
				return true;
			}
			iteratorClazz = iteratorClazz.getSuperclass();
		}
		return false;
	}

	@Override
	public void writeTo(CawException exception, Class<?> clazz, Type type, Annotation[] annotaion, MediaType mediaType, MultivaluedMap<String, Object> header, OutputStream out) throws IOException, WebApplicationException {
		out.write(stringify(exception, mediaType).getBytes());
		out.flush();
		out.close();
	}

	private static String stringify(CawException exception, MediaType type) {
		if (MediaType.APPLICATION_JSON_TYPE.equals(type)) {
			try {
				JSONObject object = new JSONObject();

				object.put(EXCEPTION_CLASS, exception.getClass().getSimpleName());
				object.put(MESSAGE, exception.getMessage());

				for (StackTraceElement element : exception.getStackTrace()) {
					object.accumulate(STACK_TRACE, element.toString());
				}

				JSONObject result = new JSONObject();
				result.put(EXCEPTION, object);

				return result.toString();
			} catch (JSONException e) {
				LOGGER.error("Failed to convert CAW exception to JSON", e);
				return "";
			}
		}
		return "";
	}
}
